

#define     VIN          3.3f      ///<     [V]
#define     VBTY_APIN    2
#define     VCH_APIN     3
#define     LED1_DPIN    9
#define     LED2_DPIN    10
#define     LED3_DPIN    11
#define     PWM_DPIN     3


// returns smoothed battery voltage
float getAvePinVoltage(int _iPin, unsigned long &_rLastSampleTimeMs)
{
    unsigned long uTime = millis();
    float fAlpha = min((uTime - _rLastSampleTimeMs) * 0.0001, 1.0);
    float fBeta = 1.0 - fAlpha;
    
    float fV = (7.0 / 1.0) * analogRead(_iPin) / 1024.0 * VIN;
    static float fVave = fV;
    
    fVave = fBeta * fVave + fAlpha * fV;
    return fVave;
}

// set output LEDs to show bty level
void setLedLevel(float _fV)
{
  if (_fV < 12)
  {
    digitalWrite(LED1_DPIN, (millis() / 500) % 2);
    digitalWrite(LED2_DPIN, LOW);
    digitalWrite(LED3_DPIN, LOW);
  }
  else
  {
    analogWrite(LED1_DPIN, _fV > 12 ? 96 : 0);
    analogWrite(LED2_DPIN, _fV > 12.5 ? 96 : 0);
    analogWrite(LED3_DPIN, _fV > 13 ? 96 : 0);
  }
}

// set output LEDs to running/charging
void setLedRunning()
{
  static int i = 0;
  i = (millis() / 1000) % 4;
    
  digitalWrite(LED1_DPIN, i >= 1);
  digitalWrite(LED2_DPIN, i >= 2);
  digitalWrite(LED3_DPIN, i >= 3);
}

// set output LEDs to running/charging
void setLedAll()
{
  digitalWrite(LED1_DPIN, HIGH);
  digitalWrite(LED2_DPIN, HIGH);
  digitalWrite(LED3_DPIN, HIGH);
}

// handle charging logic
void doCharging()
{
  static bool bChInputHigh = false;
  static bool bVbtyFull = false;
  static bool bVbtyMax = false;
  static unsigned long uLastStateTime = 0;
  static unsigned long uLastChSampleTime = 0;
  static unsigned long uLastBtySampleTime = 0;
  static int iChPwm = 0;
  
  float fVch = getAvePinVoltage(VCH_APIN, uLastChSampleTime);
  float fVbty = getAvePinVoltage(VBTY_APIN, uLastBtySampleTime);
  float fDiff = fVch - fVbty;

  // check for state updates
  if (millis() > uLastStateTime + 1000)
  {
    bChInputHigh = (fDiff > 0.0) && (fVch > 13.0);
    bVbtyFull = fVbty > 13.4;
    bVbtyMax = (fVbty > 13.6) && (bVbtyFull == true);
    uLastStateTime = millis();
    
    // debugging
    Serial.print(fVbty);
    Serial.print(" ");
    Serial.print(fVch);
    Serial.println("");
  }
  
  // perform charging
  if ( (bChInputHigh == true) &&
       (bVbtyFull == false) &&
       (bVbtyMax == false) )
  {
    iChPwm = iChPwm + (fDiff > 0.5 ? 1 : -1);
    setLedRunning();
  }
  else if ( (bChInputHigh == true) &&
            (bVbtyFull == true) &&
            (bVbtyMax == false) )
  {
    iChPwm = iChPwm + (fDiff > 0.5 ? 1 : -1);
    setLedAll();
  }
  else if ( (bChInputHigh == true) &&
            (bVbtyFull == true) &&
            (bVbtyMax == true) )
  {
    iChPwm = 0;
    setLedAll();
  }
  else if (bChInputHigh == false)
  {
    iChPwm = 0;
    setLedLevel(fVbty);
  }
  
  iChPwm = min(255, iChPwm);
  iChPwm = max(0, iChPwm);
  analogWrite(PWM_DPIN, iChPwm);
}

// setup arduino
void setup()
{
  // set Fio pins 4 to 13 as low outputs to save power (leaves RX, TX alone)
  for (unsigned char i = 2; i <= 13; i++)
  {
      pinMode(i, OUTPUT);
      digitalWrite(i, LOW);
  }

  Serial.begin(9600);
}

// do the work
void loop() 
{
  //
  doCharging();
  
  // wait a little
  delay(10);
}
